using System;
using System.IO;
using System.Text;
using System.Threading;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.IO;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.IO;
using WCR.CONVERT;


namespace FISHY_BRAINS
{
    public class Program
    {
        public static class DS18B20
        {
            public const byte SearchROM = 0xF0;
            public const byte ReadROM = 0x33;
            public const byte MatchROM = 0x55;
            public const byte SkipROM = 0xCC;
            public const byte AlarmSearch = 0xEC;
            public const byte StartTemperatureConversion = 0x44;
            public const byte ReadScratchPad = 0xBE;
            public const byte WriteScratchPad = 0x4E;
            public const byte CopySratchPad = 0x48;
            public const byte RecallEEPROM = 0xB8;
            public const byte ReadPowerSupply = 0xB4;
        }
        static OneWire owtemp = new OneWire((Cpu.Pin)FEZ_Pin.Digital.Di7);
        static OneWire ow = new OneWire((Cpu.Pin)FEZ_Pin.Digital.Di6);
        static PWM white = new PWM((PWM.Pin)FEZ_Pin.PWM.Di10);
        static PWM blue = new PWM((PWM.Pin)FEZ_Pin.PWM.Di9);
        static PWM test = new PWM((PWM.Pin)FEZ_Pin.PWM.Di8);
        static long whiteDurr = 10 * TimeSpan.TicksPerMinute;
        static long blueDurr = 5 * TimeSpan.TicksPerMinute;
        public static void Main()
        {
            readOWbus();             // Dump all one wire device addresses (only for setup)
            Thread RTC = new Thread(RTC_init);
            Thread Uptime = new Thread(Uptime_init);
            Thread Timer = new Thread(Check_Time);
            //bool ledState = false;
            //OutputPort led = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, ledState);
            Uptime.Start();
            RTC.Start();
            Timer.Start();
            //byte[] OW_Address1 = new byte[8] { 0x28, 0x92, 0x5B, 0x24, 0x03, 0x00, 0x00, 0xBE }; //DS18B20
            //byte[] OW_Address2 = new byte[8] { 0x28, 0x00, 0x44, 0x24, 0x03, 0x00, 0x00, 0x72 }; //DS18B20
            //byte[] OW_Address3 = new byte[8] { 0x28, 0x92, 0x5B, 0x24, 0x03, 0x00, 0x00, 0xBE }; //DS18B20
            //byte[] OW_Address4 = new byte[8] { 0x28, 0x00, 0x44, 0x24, 0x03, 0x00, 0x00, 0x72 }; //DS18B20
            //int i = 0;
            while (true)
            {
                byte o = 0;
                int dirr = 1;
                while (true)
                {
                    test.Set(5000, o);
                    Thread.Sleep(500);
                    if (o == 0 || o == 100)
                    {
                        Debug.Print(getTemp().ToString("F2")); // This works! no idea why... =/ But, it does only seem to give whole numbers (even with 2 decimal places shown), like 23.00. 
                        Thread.Sleep(10000); // Added to actually wait when reaching 100 and 0, instead of 99 and 1 like in loop waits.
                    }
                    o = (byte)(o + dirr);
                    if (o >= 100)
                    {
                        dirr = -1;
                        Debug.Print("Going down.");
                    }
                    if (o <= 0)
                    {
                        dirr = 1;
                        Debug.Print("Going up.");
                    }
                }
            }
        }

        public static float getTemp()
        {
            ushort temp;
            float tempf = 0;
            if (owtemp.Reset())
            {
                owtemp.WriteByte(0xCC);
                owtemp.WriteByte(0x44);

                if (owtemp.ReadByte() == 0)
                {
                    owtemp.Reset();
                    owtemp.WriteByte(0xCC);
                    owtemp.WriteByte(0xBE);

                    temp = owtemp.ReadByte();
                    temp |= (ushort)(owtemp.ReadByte() << 8);
                    tempf = (float) (temp / 16);

                    //tempf = (float)((1.80 * (temp / 16.00)) + 32.00);
                }
            }
            return tempf;
        }

                    /****************************** OneWire sequence *********************************/
                    //Debug.Print(i.ToString() + " Temperature DS18B20_1 = " + getTempCaddr(OW_Address1).ToString("F2") + " Celcius");
                    //Debug.Print(i.ToString() + " Temperature DS18B20_1 = " + getTempFaddr(OW_Address1).ToString("F2") + " Fahrenheit");
                    //Debug.Print(i.ToString() + " Temperature DS18B20_2 = " + getTempCaddr(OW_Address2).ToString("F2") + " Celcius");
                    //Debug.Print(i.ToString() + " Temperature DS18B20_2 = " + getTempFaddr(OW_Address2).ToString("F2") + " Fahrenheit");
                    /***************************** End One Wire sequence *****************************/
                    //i++;

                    // toggle LED state
                    //ledState = !ledState;
                    //led.Write(ledState);



        /// Get Celsius temp from sensor
       /* public static float getTempCaddr(byte[] addr)
        {
            ushort temp;
            float tempc = 0; // temperature in degrees Celsius
            if (ow.Reset())
            {
                ow.WriteByte(DS18B20.MatchROM); // Match ROM
                ow.Write(addr, 0, 8);
                ow.WriteByte(DS18B20.StartTemperatureConversion); // Start temperature conversion
                if (ow.ReadByte() == 0)
                {
                    ow.Reset();
                    ow.WriteByte(DS18B20.MatchROM); // Match ROM
                    ow.Write(addr, 0, 8);
                    ow.WriteByte(DS18B20.ReadScratchPad); // Read Scratchpad
                    Thread.Sleep(1000); //wait for conversion to finish
                    temp = ow.ReadByte();                   // LSB
                    temp |= (ushort)(ow.ReadByte() << 8);   // MSB
                    tempc = (float)(temp / 16);
                }
                else
                {
                    Debug.Print("Device is not detected.");
                }
            }
            return tempc;
        }
        /// Get Fahrenheit temp from sensor
        public static float getTempFaddr(byte[] addr)
        {
            ushort temp;
            float tempf = 0; // temperature in degrees Fahrenheit
            if (ow.Reset())
            {
                ow.WriteByte(DS18B20.MatchROM); // Match ROM
                ow.Write(addr, 0, 8);
                ow.WriteByte(DS18B20.StartTemperatureConversion); // Start temperature conversion
                if (ow.ReadByte() == 0)
                {
                    ow.Reset();
                    ow.WriteByte(DS18B20.MatchROM); // Match ROM
                    ow.Write(addr, 0, 8);
                    ow.WriteByte(DS18B20.ReadScratchPad); // Read Scratchpad
                    Thread.Sleep(1000); //wait for conversion to finish
                    temp = ow.ReadByte();                   // LSB
                    temp |= (ushort)(ow.ReadByte() << 8);   // MSB
                    tempf = (float)((1.80 * (temp / 16.00)) + 32.00);
                }
                else
                {
                    Debug.Print("Device is not detected.");
                }
            }
            return tempf;
        }
        
        */ // Add/remove here to kill readOWbus()
        
        // This is needed to read the code off the sensors, but it doesn't work.  
        // Dump all one wire device addresses
        public static void readOWbus()
        {
            int i;
            int OW_number;
            byte[] OW_Address = new byte[8];
            // Connect conversion class
            c_Convert Convert = new c_Convert();
            if (ow.Reset())
            {
                OW_number = 0;
                while (ow.Search_GetNextDevice(OW_Address))
                {
                    ow.Read(OW_Address, 0, 8);
                    for (int x = 0; x < 8; x++)
                    {
                        Debug.Print(OW_Address[x].ToString());
                    }
                    //for (i = 0; i < 8; i++) Debug.Print(OW_Address[i] + ":");
                    for (i = 0; i < 8; i++) Debug.Print("0x" + (Convert.TwoByte_ToHex((byte)OW_Address[i])) + ":");
                    Debug.Print("\n");
                    // Check if device is DS18B20 family
                    if (OW_Address[0] == 0x10) { Debug.Print("Device is a DS18S20 family device."); }
                    else if (OW_Address[0] == 0x28) { Debug.Print("Device is a DS18B20 family device."); }
                    else { Debug.Print("Device family is not recognized: " + OW_Address[0].ToString() + " decimal address"); }
                    // Next device
                    OW_number++;
                }
                // Number of one-wire devices
                Debug.Print("Found: " + OW_number + " devices");
            }
        }
        
        // I haven't actually tested this, as i couldn't get the temp probes to read anything, there is no point trying to make it read them async like this code is supposed to do.
        /*public class ThermometerWatcher
        {
            private readonly Thermometer _Thermometer;
            private readonly TimeSpan _Interval = new TimeSpan(TimeSpan.TicksPerSecond);
            private readonly Thermometer.UnitConverter _Converter = d => d;
            private Timer _Timer;
            private long _Data;
            private float _Temperature;
            public delegate void TemperatureDataChangeDelegate(long from, long to);
            public event TemperatureDataChangeDelegate TemperatureDataChanged;
            public delegate void TemperatureChangeDelegate(float from, float to);
            public event TemperatureChangeDelegate TemperatureChanged;
            public ThermometerWatcher(Thermometer thermometer) : this(thermometer, Thermometer.DataToF) { }
            public ThermometerWatcher(Thermometer thermometer, Thermometer.UnitConverter converter)
            {
                _Thermometer = thermometer;
                _Converter = converter;
            }
            public void Start()
            {
                if (_Timer == null)
                {
                    _Timer = new Timer(state =>
                    {
                        long data = _Thermometer.ReadTemperatureData();
                        if (_Data != data)
                        {
                            if (TemperatureDataChanged != null)
                                TemperatureDataChanged(_Data, data);
                            _Data = data;
                            float temp = _Converter(data);
                            if (_Temperature != temp)
                            {
                                if (TemperatureChanged != null)
                                    TemperatureChanged(_Temperature, temp);
                                _Temperature = temp;
                            }
                        }
                    }, this, TimeSpan.Zero, _Interval);
                }
                else _Timer.Change(TimeSpan.Zero, _Interval);
            }
            public void Stop()
            {
                _Timer.Change(0, 0);
            }
    */
        // This all works, its just a lot of mess i think, as well as plenty of extra debug stuff.
        private static void Check_Time()
        {
            long startHB = 19 * TimeSpan.TicksPerHour;
            long startMB = 0 * TimeSpan.TicksPerMinute;
            long startTimeBlue = TimeSpan.TicksPerDay - startHB - startMB;
            long startHW = 19 * TimeSpan.TicksPerHour;
            long startMW = 15 * TimeSpan.TicksPerMinute;
            long startTimeWhite = TimeSpan.TicksPerDay - startHW - startMW;
            long endHW = 19 * TimeSpan.TicksPerHour;
            long endMW = 45 * TimeSpan.TicksPerMinute;
            long endTimeWhite = TimeSpan.TicksPerDay - endHW - endMW;
            long endHB = 20 * TimeSpan.TicksPerHour;
            long endMB = 0 * TimeSpan.TicksPerMinute;
            long endTimeBlue = TimeSpan.TicksPerDay - endHB - endMB;
            Debug.Print(TimeSpan.FromTicks(startHB + startMB).ToString() + " blue start time. " + TimeSpan.FromTicks(blueDurr).Minutes.ToString() + " minute ramp duration.");
            Debug.Print(TimeSpan.FromTicks(startHW + startMW).ToString() + " white start time. " + TimeSpan.FromTicks(whiteDurr).Minutes.ToString() + " minute ramp duration.");
            Debug.Print(TimeSpan.FromTicks(startTimeBlue - endTimeBlue).ToString() + " blue light cycle total duration. " + TimeSpan.FromTicks(startTimeBlue - 2 * blueDurr - endTimeBlue).ToString() + " blue light max intensity duration.");
            Debug.Print(TimeSpan.FromTicks(startTimeWhite - endTimeWhite).ToString() + " white light cycle total duration. " + TimeSpan.FromTicks(startTimeWhite - 2 * whiteDurr - endTimeWhite).ToString() + " white light max intensity duration.");
            Debug.Print(TimeSpan.FromTicks(endHW + endMW).ToString() + " white end time.");
            Debug.Print(TimeSpan.FromTicks(endHB + endMB).ToString() + " blue end time.");
            DateTime midnight = new DateTime(2011, 9, 1, 0, 0, 0);
            bool doneBlueUp = false;
            bool doneWhiteUp = false;
            bool doneWhiteDown = false;
            bool doneBlueDown = false;
            while (true)
            {
                DateTime currentTime = DateTime.Now;
                long ticksToGo = midnight.Ticks - currentTime.Ticks;
                for (; ticksToGo <= 0; ticksToGo = ticksToGo + TimeSpan.TicksPerDay) // Amazingly this works so well i forgot that it was here (and that the start date for midnight is 9/1)...
                {
                    midnight = midnight.AddDays(1);
                    //Debug.Print(TimeSpan.FromTicks(ticksToGo).ToString());
                    doneBlueUp = false;
                    doneWhiteUp = false;
                    doneWhiteDown = false;
                    doneBlueDown = false;
                }                      
                if (ticksToGo <= startTimeBlue && ticksToGo > endTimeBlue + blueDurr && ticksToGo > 0 && doneBlueUp == false)
                {
                    Debug.Print(TimeSpan.FromTicks(TimeSpan.TicksPerDay - ticksToGo).ToString() + " Ramping up blue.");
                    Thread RampUpBlue = new Thread(Ramp_Up_Blue);
                    RampUpBlue.Start();
                    doneBlueUp = true;
                }
                if (ticksToGo <= startTimeWhite && ticksToGo > endTimeWhite + whiteDurr && ticksToGo > 0 && doneWhiteUp == false)
                {
                    Debug.Print(TimeSpan.FromTicks(TimeSpan.TicksPerDay - ticksToGo).ToString() + " Ramping up white.");
                    Thread RampUpWhite = new Thread(Ramp_Up_White);
                    RampUpWhite.Start();
                    doneWhiteUp = true;
                }
                if (ticksToGo <= endTimeWhite + whiteDurr && ticksToGo > 0 && doneWhiteDown == false)
                {
                    Debug.Print(TimeSpan.FromTicks(TimeSpan.TicksPerDay - ticksToGo).ToString() + " Ramping down white.");
                    Thread RampDownWhite = new Thread(Ramp_Down_White); 
                    RampDownWhite.Start();
                    doneWhiteDown = true;
                }
                if (ticksToGo <= endTimeBlue + blueDurr && ticksToGo > 0 && doneBlueDown == false)
                {
                    Debug.Print(TimeSpan.FromTicks(TimeSpan.TicksPerDay - ticksToGo).ToString() + " Ramping down blue.");
                    Thread RampDownBlue = new Thread(Ramp_Down_Blue);
                    RampDownBlue.Start();
                    doneBlueDown = true;
                }
                Thread.Sleep(5000);
            }
        }
        // Something like this is what i would like to get, but it doesn't work... Or well, it might, but i don't know how to start it as a separate thread with the parameters it needs.
        public enum Direction : int
        {
            Up = 1,
            Down = -1,
        }
        public static void Ramp(byte start, byte end, Direction dir, int dur)
        {
            byte x = start;
            int durStep = dur * 60 * 10; // Duration in minutes to steps in ms
            for (; start != end; start = (byte)(start + dir))
            {
                white.Set(5000, x); //How do i make color on this PWM this a variable.
                x = (byte)(x + dir);
                Thread.Sleep(durStep);
            }
        }
        // These work fine, its just silly i thin to have to have 4 of them. Durations found at start of program.
        public static void Ramp_Up_Blue()
        {
            int durr = (int) (blueDurr / TimeSpan.TicksPerMillisecond);
            for (byte b = 0; b < 100; b++)
            {
                blue.Set(5000, b);
                Thread.Sleep(durr);
            }
        }
        public static void Ramp_Down_Blue()
        {
            int durr = (int)(blueDurr / TimeSpan.TicksPerMillisecond);
            for (byte b = 100; b > 0; b--)
            {
                blue.Set(5000, b);
                Thread.Sleep(durr);
            }
        }
        public static void Ramp_Up_White()
        {
            int durr = (int)(whiteDurr / TimeSpan.TicksPerMillisecond);
            for (byte w = 0; w < 100; w++)
            {
                white.Set(5000, w);
                Thread.Sleep(durr);
            }
        }
        public static void Ramp_Down_White()
        {
            int durr = (int)(whiteDurr / TimeSpan.TicksPerMillisecond);
            for (byte w = 100; w > 0; w--)
            {
                white.Set(5000, w);
                Thread.Sleep(durr);
            }
        }
        //Everything here seems to work, i haven't really done anything to it.
        private static void RTC_init()
        {
            DateTime DT;
            try
            {
                DT = RealTimeClock.GetTime();
                Utility.SetLocalTime(DT);
                Debug.Print("System time set successfully.");
            }
            catch // If the time is not good due to powerloss or being a new system an exception will be thrown and a new time will need to be set
            {
                Debug.Print("RTC time corrupt. Default time set.");
                DT = new DateTime(2012, 1, 1, 0, 0, 0); // This will set a time for the real-time clock to 1/1/2012
                RealTimeClock.SetTime(DT); //This will set the hardware real-time clock to what is in DT
            }
            if (DT.Year < 2011)
            {
                Debug.Print("RTC time unreasonable.");
            }
            //DT = new DateTime(2011, 10, 02, 1, 30, 55); // This will set the clock
            //RealTimeClock.SetTime(DT); //This will set the hardware Real-time Clock to what is in DT
        }
        private static void Uptime_init()
        {
            bool ShowUptime = true; // calculate uptime?
            int UpdateUptimeLoop = 5 * 60000; // Minutes in ms
            System.TimeSpan uptimeCount;
            DateTime startupTime = RealTimeClock.GetTime(); // defining startup time variable
            if (ShowUptime == true)
            {
                while (true)
                {
                    uptimeCount = DateTime.Now.Subtract(startupTime); // Counting System Uptime and sending out to Debug Print
                    Debug.Print("System uptime: " + uptimeCount.Hours.ToString() + " hours " + uptimeCount.Minutes.ToString() + " minutes."); // Counting System Uptime and sending out to Debug Print 
                    Thread.Sleep(UpdateUptimeLoop);
                }
            }
        }
    }
}

        